import { Button, Tag, App } from 'antd';
import React, { useCallback, useMemo } from 'react';
import { useContextSelector } from 'use-context-selector';

import { WholeResponseContent } from '@/components/chat-box/WholeResponseContent';
import EmptyTip from '@/components/EmptyTip';
import { SvgIcon } from '@/components/icon';
import AvatarDisplay from '@/components/work-modal/components/AvatarDisplay';
import { useEditTitle } from '@/hooks/common/useEditTitle';
import { useConfirm } from '@/hooks/use-confirm';
import useDebug from '@/hooks/use-debug';
import { useWorkflowUtils } from '@/hooks/use-workflow/useUtils';
import { t } from '@/locales/i18n';
import { LOGO_ICON } from '@/pages/chat/tools/constants';
import { getNanoid } from '@/pages/chat/tools/tools';
import { FlowNodeTypeEnum } from '@/types/workflow/constant';
import { FlowNodeItemType } from '@/types/workflow/node';
import { checkNodeWithDetails, storeNode2FlowNode } from '@/utils/workflow/utils';

import { WorkflowContext } from '../../../context';

import { ConnectionSourceHandle, ConnectionTargetHandle } from './Handle/ConnectionHandle';
import { ToolTargetHandle } from './Handle/ToolHandle';
import './index.module.scss';
import RenderFoldedOutput from './RenderOutput/RenderFoldedOutput';

import NodeCQNodeFolded from '../NodeCQnode/NodeCQNodeFolded';
import NodeIfElseNodeFolded from '../NodeIfElse/NodeIfElseNodeFolded';
import NodeVariableUpdateFolded from '../NodeVariableUpdate/NodeVariableUpdateFolded';

import { AnimatePresence, m } from 'framer-motion';

import NodeAnswerFolded from '../NodeAnswer/NodeAnswerFolded';
import NodeUserSelectFolded from '../NodeUserSelect/NodeUserSelectFolded';

import { useSystemStore } from '@/store/useSystemStore';
import { NodeStatusEnum } from '@/types/app/type';

const nodeFoldedComponents = {
  [FlowNodeTypeEnum.classifyQuestion]: NodeCQNodeFolded,
  [FlowNodeTypeEnum.ifElseNode]: NodeIfElseNodeFolded,
  [FlowNodeTypeEnum.variableUpdate]: NodeVariableUpdateFolded,
  [FlowNodeTypeEnum.answerNode]: NodeAnswerFolded,
  [FlowNodeTypeEnum.userSelect]: NodeUserSelectFolded,
};

export type NodeCardProps = FlowNodeItemType & {
  children?: React.ReactNode | React.ReactNode[] | string;
  minW?: string | number;
  maxW?: string | number;
  minH?: string | number;
  w?: string | number;
  h?: string | number;
  selected?: boolean;
  menuForbid?: {
    debug?: boolean;
    copy?: boolean;
    delete?: boolean;
  };
  className?: string;
  customStyle?: React.CSSProperties;
};

function NodeCard(props: NodeCardProps) {
  const { message } = App.useApp();

  const {
    children,
    avatar = LOGO_ICON,
    name = t('core.module.template.UnKnow Module'),
    intro,
    minW = '300px',
    maxW = '600px',
    minH = 0,
    w = 'full',
    h = 'full',
    nodeId,
    selected,
    menuForbid,
    isTool = false,
    isError = false,
    debugResult,
    isFolded,
    customStyle,
    className,
    outputs,
    inputs,
    flowNodeType,
  } = props;

  const nodeList = useContextSelector(WorkflowContext, (v) => v.nodeList);
  const nodes = useContextSelector(WorkflowContext, (v) => v.nodes);
  const edges = useContextSelector(WorkflowContext, (v) => v.edges);

  const setHoverNodeId = useContextSelector(WorkflowContext, (v) => v.setHoverNodeId);
  const onUpdateNodeError = useContextSelector(WorkflowContext, (v) => v.onUpdateNodeError);
  const onChangeNode = useContextSelector(WorkflowContext, (v) => v.onChangeNode);
  const onResetNode = useContextSelector(WorkflowContext, (v) => v.onResetNode);
  const { llmModelList } = useSystemStore();

  const checkNode = useMemo(() => {
    const detail = checkNodeWithDetails({ node: props, nodes, edges, llmModelList });
    return detail;
  }, [edges, llmModelList, nodes, props]);

  // custom title edit
  const { onOpenModal: onOpenCustomTitleModal, EditModal: EditTitleModal } = useEditTitle({
    title: t('common.Custom Title'),
    placeholder: t('module.Custom Title Tip') || '',
  });

  const showToolHandle = useMemo(
    () =>
      (isTool ||
        flowNodeType === FlowNodeTypeEnum.tool ||
        flowNodeType === FlowNodeTypeEnum.toolSet) &&
      !!nodeList.find((item) => item?.flowNodeType === FlowNodeTypeEnum.tools),
    [isTool, nodeList, flowNodeType],
  );

  // Current node and parent node
  const { node, parentNode } = useMemo(() => {
    const node = nodeList.find((node) => node.nodeId === nodeId);
    const parentNode = node?.parentNodeId
      ? nodeList.find((n) => n.nodeId === node?.parentNodeId)
      : undefined;

    return { node, parentNode };
  }, [nodeList, nodeId]);

  /* Node header */
  const Header = useMemo(() => {
    const showHeader = node?.flowNodeType !== FlowNodeTypeEnum.comment;

    return (
      <div className="relative rounded-t-2xl">
        {/* debug */}
        {showHeader && (
          <div className="">
            {/* tool target handle */}
            <ToolTargetHandle show={showToolHandle} nodeId={nodeId} />
            <div className="relative rounded-t-2xl p-4">
              <div
                className="group flex cursor-pointer items-center"
                onClick={() =>
                  onChangeNode({
                    nodeId,
                    type: 'attr',
                    key: 'isFolded',
                    value: !isFolded,
                  })
                }
              >
                <div className="flex items-center gap-2">
                  <div className="relative">
                    <div className="flex h-12 w-12 transform items-center justify-center rounded-2xl transition-transform duration-200 group-hover:scale-105">
                      <AvatarDisplay
                        src={`${avatar!}`}
                        className="h-[30px] max-h-[30px] w-[30px] max-w-[30px]"
                        size={30}
                      />
                    </div>
                  </div>
                  <div className="flex flex-col">
                    <div className="flex items-center gap-1">
                      <h3 className="text-base font-font-weight-600 text-text-5">
                        {t(name as any)}
                      </h3>

                      <SvgIcon
                        className="controller-rename hidden cursor-pointer"
                        icon="ic-edit"
                        color="#4E5969"
                        size={14}
                        onClick={(event) => {
                          event.stopPropagation();
                          onOpenCustomTitleModal({
                            defaultVal: t(name as any),
                            onSuccess: (e) => {
                              if (!e) {
                                message.warning(t('modules.Title is required'));
                                return;
                              }
                              onChangeNode({
                                nodeId,
                                type: 'attr',
                                key: 'name',
                                value: e,
                              });
                            },
                          });
                        }}
                      />
                    </div>
                    <div className="mt-1 flex items-center gap-2">
                      <span className="text-xs font-medium text-text-4">{t(intro as any)}</span>
                    </div>
                  </div>
                </div>
                <div className="ml-auto flex items-center">
                  {node?.status && node?.status === NodeStatusEnum.deleted && (
                    <Tag color="red" style={{ margin: 0, borderRadius: '6px' }}>
                      <div className="flex items-center gap-1 py-1">
                        <div className="h-1 w-1 animate-pulse rounded-full bg-[#FC5151]" />
                        <span className="text-xs font-medium text-[#FC5151]">已失效</span>
                      </div>
                    </Tag>
                  )}
                  <div
                    onClick={() => {
                      onChangeNode({
                        nodeId,
                        type: 'attr',
                        key: 'isFolded',
                        value: !isFolded,
                      });
                    }}
                    className="flex h-8 w-8 items-center justify-center rounded-lg"
                  >
                    {node?.flowNodeType !== FlowNodeTypeEnum.stopTool && (
                      <div
                        className={`flex h-4 w-4 items-center justify-center text-gray-500 transition-all duration-300 group-hover:text-gray-700 ${
                          isFolded ? 'rotate-90' : ''
                        }`}
                      >
                        <SvgIcon
                          // color={`${selected || isError ? 'white' : 'black'} `}
                          color="#86909C"
                          icon="arrow-right"
                          size={16}
                        />
                      </div>
                    )}
                  </div>
                </div>
              </div>
            </div>
          </div>
        )}
        <MenuRender nodeId={nodeId} menuForbid={menuForbid} nodeList={nodeList} />
      </div>
    );
  }, [
    node?.flowNodeType,
    showToolHandle,
    nodeId,
    isFolded,
    avatar,
    name,
    intro,
    menuForbid,
    nodeList,
    onChangeNode,
    onOpenCustomTitleModal,
    message,
    node?.status,
  ]);

  const RenderHandle = useMemo(() => {
    return (
      <>
        <ConnectionSourceHandle nodeId={nodeId} isFoldNode={false} />
        <ConnectionTargetHandle nodeId={nodeId} />
      </>
    );
  }, [nodeId]);

  const RenderFolded = useMemo(() => {
    return (
      <>
        {isFolded &&
          node?.flowNodeType !== FlowNodeTypeEnum.variableUpdate &&
          node?.flowNodeType !== FlowNodeTypeEnum.answerNode && (
            <RenderFoldedOutput outputs={outputs} />
          )}
        {isFolded &&
          node?.flowNodeType &&
          (() => {
            const FoldedComponent =
              nodeFoldedComponents[node.flowNodeType as keyof typeof nodeFoldedComponents];
            return FoldedComponent && <FoldedComponent {...props} />;
          })()}
      </>
    );
  }, [isFolded, node?.flowNodeType, outputs, props]);

  return (
    <m.div
      className={`${parentNode?.isFolded ? 'hidden' : ''} ${
        node?.flowNodeType === FlowNodeTypeEnum.loopStart ? 'nodrag' : ''
      }
        relative rounded-2xl shadow-xl transition-all duration-100 hover:shadow-2xl [&:hover_.controller-debug]:opacity-100 [&:hover_.controller-menu]:-translate-y-2 [&:hover_.controller-menu]:opacity-100 [&:hover_.controller-rename]:block`}
      style={{
        ...customStyle,
        minWidth: minW,
        maxWidth: maxW,
        minHeight: minH,
        width: w,
        background: 'white',
        boxShadow: (() => {
          if (isError && selected) {
            return 'inset 0 0 0 2px #FC5151, 0 25px 50px -12px rgba(0, 0, 0, 0.25)';
          }
          if (selected) {
            return 'inset 0 0 0 2px #1B6DFA, 0 25px 50px -12px rgba(0, 0, 0, 0.25)';
          }
          return 'inset 0 0 0 0px #E5E6EB, 0 25px 50px -12px rgba(0, 0, 0, 0.25)';
        })(),
      }}
      // transition={{
      //   scale: { duration: 0.2 },
      //   default: { duration: 0 }, // 禁用其他属性的动画
      // }}
      onMouseEnter={() => setHoverNodeId(nodeId)}
      onMouseLeave={() => setHoverNodeId(undefined)}
      onMouseDownCapture={isError ? () => onUpdateNodeError(nodeId, false) : undefined}
    >
      <div>
        {Header}
        <NodeDebugResponse nodeId={nodeId} debugResult={debugResult} />
        <AnimatePresence mode="wait">
          {!isFolded ? (
            <m.div
              key="expanded"
              initial={{ height: 0, opacity: 0 }}
              animate={{ height: 'auto', opacity: 1 }}
              exit={{ height: 0, opacity: 0 }}
              transition={{ duration: 0.2 }}
              // style={{ overflowY: 'hidden' }}
            >
              {children}
            </m.div>
          ) : (
            <m.div
              key="folded"
              initial={{ opacity: 0 }}
              animate={{ opacity: 1 }}
              exit={{ opacity: 0 }}
              transition={{ duration: 0.2 }}
            >
              {RenderFolded}
            </m.div>
          )}
        </AnimatePresence>
        {RenderHandle}
        {/* {RenderToolHandle} */}
        <EditTitleModal maxLength={20} />
      </div>
    </m.div>
  );
}

export default React.memo(NodeCard);

const Menu = ({
  nodeId,
  menuForbid,
  nodeList,
}: {
  nodeId: string;
  menuForbid?: NodeCardProps['menuForbid'];
  nodeList: FlowNodeItemType[];
}) => {
  const { openDebugNode, DebugInputModal, open } = useDebug();

  const { setNodes, setEdges } = useContextSelector(WorkflowContext, (v) => v);
  const { computedNewNodeName } = useWorkflowUtils();

  const onCopyNode = useCallback(
    (nodeId: string) => {
      setNodes((state) => {
        const node = state.find((node) => node.id === nodeId);
        if (!node) return state;
        const template = {
          avatar: node.data.avatar,
          name: computedNewNodeName({
            templateName: node.data.name,
            flowNodeType: node.data.flowNodeType,
            pluginId: node.data.pluginId,
          }),
          intro: node.data.intro,
          flowNodeType: node.data.flowNodeType,
          inputs: node.data.inputs,
          outputs: node.data.outputs,
          showStatus: node.data.showStatus,
          status: node.data.status,
          pluginId: node.data.pluginId,
          version: node.data.version,
        };

        return [
          ...state.map((item) => ({
            ...item,
            selected: false,
          })),
          storeNode2FlowNode({
            item: {
              flowNodeType: template.flowNodeType,
              avatar: template.avatar,
              name: template.name,
              intro: template.intro,
              nodeId: getNanoid(),
              position: { x: node.position.x + 200, y: node.position.y + 50 },
              showStatus: template.showStatus,
              status: template.status,
              pluginId: template.pluginId,
              inputs: template.inputs,
              outputs: template.outputs,
              version: template.version,
            },
            selected: true,
            parentNodeId: undefined,
            t,
          }),
        ];
      });
    },
    [computedNewNodeName, setNodes],
  );
  const onDelNode = useCallback(
    (nodeId: string) => {
      // Remove node and its child nodes
      setNodes((state) =>
        state.filter((item) => item.data.nodeId !== nodeId && item.data.parentNodeId !== nodeId),
      );

      // Remove edges connected to the node and its child nodes
      const childNodeIds = nodeList
        .filter((node) => node.parentNodeId === nodeId)
        .map((node) => node.nodeId);
      setEdges((state) =>
        state.filter(
          (edge) =>
            edge.source !== nodeId &&
            edge.target !== nodeId &&
            !childNodeIds.includes(edge.target) &&
            !childNodeIds.includes(edge.source),
        ),
      );
    },
    [nodeList, setEdges, setNodes],
  );

  const Render = useMemo(() => {
    const menuList = [
      ...(menuForbid?.debug
        ? []
        : [
            {
              icon: 'debug',
              bg: 'bg-gradient-to-r from-blue-400 to-green-400',
              label: t('core.workflow.Debug'),
              variant: 'default',
              // TODO 后续实现
              onClick: () => openDebugNode({ entryNodeId: nodeId }),
              // onClick: () => {},
            },
          ]),
      ...(menuForbid?.copy
        ? []
        : [
            {
              icon: 'copy',
              bg: 'bg-gradient-to-r from-purple-500 to-pink-400',
              label: t('common.Copy'),
              variant: 'default',
              onClick: () => onCopyNode(nodeId),
            },
          ]),
      ...(menuForbid?.delete
        ? []
        : [
            {
              icon: 'delete-outlined',
              bg: 'bg-gradient-to-r from-orange-700 to-orange-300',
              label: t('common.Delete'),
              variant: 'danger',
              onClick: () => onDelNode(nodeId),
            },
          ]),
    ];

    return (
      <>
        <div className="nodrag controller-menu absolute -top-10 right-0 flex translate-y-0 transform cursor-pointer gap-1 rounded-lg bg-white p-1 opacity-0 shadow-md duration-300">
          {menuList.map((item, i) => (
            <div key={i} className="flex items-center justify-center">
              {i !== 0 && <div className="mr-1 h-[12px] w-[1px] bg-line-2" />}
              <div
                key={item.icon}
                className="flex h-8 w-8 items-center justify-center rounded-lg bg-white transition-all hover:bg-fill-2"
                onClick={item.onClick}
              >
                <SvgIcon icon={item.icon as any} color="#4E5969" size={16} />
              </div>
            </div>
          ))}
        </div>
        {open && (
          <div className="nowheel nodrag absolute right-[-430px] top-0 z-10 max-h-max w-[420px] rounded-2xl bg-white shadow-md">
            <DebugInputModal />
          </div>
        )}
      </>
    );
  }, [
    menuForbid?.debug,
    menuForbid?.copy,
    menuForbid?.delete,
    open,
    DebugInputModal,
    openDebugNode,
    nodeId,
    onCopyNode,
    onDelNode,
  ]);

  return Render;
};

const MenuRender = React.memo(Menu);

const NodeDebugResponse = React.memo(function NodeDebugResponse({
  nodeId,
  debugResult,
}: {
  nodeId: string;
  debugResult: FlowNodeItemType['debugResult'];
}) {
  const { onChangeNode, onStopNodeDebug, onNextNodeDebug, workflowDebugData } = useContextSelector(
    WorkflowContext,
    (v) => v,
  );

  const { openConfirm, ConfirmModal } = useConfirm({
    content: t('core.workflow.Confirm stop debug'),
  });

  const RenderStatus = useMemo(() => {
    const map = {
      running: {
        bg: 'bg-[#F0F7FF]',
        border: 'border-[#BADDFF]',
        text: t('core.workflow.Running'),
        icon: 'line-md--loading-alt-loop',
      },
      success: {
        bg: 'bg-[#E8F9F2]',
        border: 'border-[#95E5C4]',
        text: t('core.workflow.Success'),
        icon: 'runSuccess',
      },
      failed: {
        bg: 'bg-[#FFF1F0]',
        border: 'border-[#FFBEBA]',
        text: t('core.workflow.Failed'),
        icon: 'runError',
      },
      skipped: {
        bg: 'bg-[#F2F3F5]',
        border: 'border-[#E5E6EB]',
        text: t('core.workflow.Skipped'),
        icon: 'runSkip',
      },
    };

    const statusData = map[debugResult?.status || 'running'];

    const response = debugResult?.response;

    const onStop = () => {
      openConfirm(onStopNodeDebug)();
    };

    return !!debugResult && !!statusData ? (
      <>
        <div
          className={`px-4 ${statusData.bg} ${statusData.border} mx-5 my-2 flex items-center rounded-2xl border py-3`}
        >
          <SvgIcon icon={statusData.icon as any} className="mr-2 w-4" />
          <div className="flex-1 font-bold text-gray-900">{statusData.text}</div>
          {debugResult.status !== 'running' && (
            <div
              className="cursor-pointer text-sm text-primary"
              onClick={() =>
                onChangeNode({
                  nodeId,
                  type: 'attr',
                  key: 'debugResult',
                  value: {
                    ...debugResult,
                    showResult: !debugResult.showResult,
                  },
                })
              }
            >
              {debugResult.showResult
                ? t('core.workflow.debug.Hide result')
                : t('core.workflow.debug.Show result')}
            </div>
          )}
        </div>
        {/* Result card */}
        {debugResult.showResult && (
          <div className="nowheel nodrag absolute right-[-430px] top-0 z-10 max-h-max w-[420px] rounded-2xl bg-white shadow-md">
            {/* Status header */}
            <div className="flex h-[54px] items-center px-4 py-3">
              <div className="flex-1 text-base font-font-weight-600">
                {t('core.workflow.debug.Run result')}
              </div>
              {workflowDebugData?.nextRunNodes.length !== 0 && (
                <Button size="small" danger onClick={onStop}>
                  {t('core.workflow.Stop debug')}
                </Button>
              )}
              {(debugResult.status === 'success' || debugResult.status === 'skipped') &&
                !debugResult.isExpired &&
                workflowDebugData?.nextRunNodes &&
                workflowDebugData.nextRunNodes.length > 0 && (
                  <Button
                    className="ml-2"
                    size="small"
                    type="primary"
                    onClick={() => onNextNodeDebug()}
                  >
                    {t('common.Next Step')}
                  </Button>
                )}
              {workflowDebugData?.nextRunNodes && workflowDebugData?.nextRunNodes.length === 0 && (
                <Button className="ml-2" size="small" type="primary" onClick={onStopNodeDebug}>
                  {t('core.workflow.debug.Done')}
                </Button>
              )}
            </div>
            {/* Response list */}
            {debugResult.status !== 'skipped' && (
              <div className="mt-1 min-h-[250px] overflow-y-auto">
                {!debugResult.message && !response && (
                  <EmptyTip text={t('core.workflow.debug.Not result')} className="pb-5 pt-2" />
                )}
                {debugResult.message && (
                  <div className="text-red-600 px-3 py-4">{debugResult.message}</div>
                )}
                {response && <WholeResponseContent activeModule={response} showDetail />}
              </div>
            )}
          </div>
        )}
      </>
    ) : null;
  }, [
    debugResult,
    nodeId,
    onChangeNode,
    onNextNodeDebug,
    onStopNodeDebug,
    openConfirm,
    workflowDebugData,
  ]);

  return (
    <>
      {RenderStatus}
      <ConfirmModal />
    </>
  );
});
